package com.culture.config.common.util;

import lombok.Synchronized;
import lombok.extern.slf4j.Slf4j;

import java.util.Objects;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicInteger;

@Slf4j
public abstract class ThreadRunnable implements Runnable {
    private final ExecutorService executor;
    private final boolean singleExecutor;
    private volatile boolean runFlag = false;
    private AtomicInteger execThreadCount = new AtomicInteger(0);   

    protected ThreadRunnable(int nThreads,boolean singleExecutor){     
        if(singleExecutor){
            nThreads = 1;
        }   
        this.singleExecutor = singleExecutor;
        this.executor = Executors.newFixedThreadPool(nThreads);        
    }   

    /**
     * check can exec task...
     * @return
     */
    protected boolean getRunFlag(){
        return this.runFlag;
    }

    /**   
     * start exec task
     * @param flag
     * @return
     */
    @Synchronized
    public boolean start(int flag){       
        if(this.executor.isShutdown() || this.executor.isTerminated()){
            log.info("executor has shutdown.");
            return false;
        }
        this.runFlag = true;
        return this.executeTask(flag);
    }

    /**
     * stop exec task
     */
    @Synchronized
    public void stop(){
        this.runFlag = false;        
    }

    /**
     * shutdown ThreadRunnable
     */
    @Synchronized
    public void shutdown(){
        this.runFlag = false;
        if(Objects.isNull(this.executor)){
            return;
        }
        if(this.executor.isShutdown() || this.executor.isTerminated()){
            return;
        }        
        this.executor.shutdown();
    }  

    @Synchronized
    private boolean executeTask(int flag) {       
        log.info("has executeTask:{}",this.execThreadCount);
        if(this.singleExecutor 
            && this.runFlag 
            && this.execThreadCount.get() > 0){
            return false;
        }         
        this.onStart(flag);
        this.executor.execute(this::run);
        return true;
    }

    @Synchronized
    private void updateRunTaskCount(int count) {
        this.execThreadCount.addAndGet(count);
    }

    /**
     * thread before start add your code to override this method
     */
    protected void onStart(int flag){}  
    
    /**
     * thread after stop for singleExecutor, add your code to override this method
     */
    protected void onStop(){}

    /**
     * thread run add your code to override this method
     * @param runIndex
     * @return
     */
    protected boolean runTask(final long startTime,final long runIndex){
        log.info("runTask:{},{}",startTime,runIndex);
        return false;
    }

    @Override
    public void run(){
        this.updateRunTaskCount(1);
        // Initialize the startTime and run index.     
        final long startTime = System.nanoTime();         
        long runIndex = 0;
        // run task by wheel 
        do {   
            if(!this.runTask(startTime,runIndex)){
                this.stop();
                break;
            }
            runIndex++;          
        } while (this.getRunFlag());
        //stop and clear
        this.updateRunTaskCount(-1);   
        //reset flag to false for singleExecutor
        if(this.singleExecutor){
            this.runFlag = false;
            this.onStop();
        }    
    }       
}
